package com.filex.ads.common;

import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;


import com.filex.ads.common.logging.Logger;

import java.util.EnumSet;

import static com.filex.ads.network.TrackingRequest.makeTrackingHttpRequest;


public class UrlHandler {

    public interface ResultActions {
        void urlHandlingSucceeded(@NonNull final String url, @NonNull final UrlAction urlAction);

        void urlHandlingFailed(@NonNull final String url,
                               @NonNull final UrlAction lastFailedUrlAction);
    }

    public static class Builder {
        @NonNull
        private EnumSet<UrlAction> supportedUrlActions = EnumSet.of(UrlAction.NOOP);
        @NonNull
        private ResultActions resultActions = EMPTY_CLICK_LISTENER;

        public Builder withSupportedUrlActions(@NonNull final UrlAction first,
                                               @Nullable final UrlAction... others) {
            this.supportedUrlActions = EnumSet.of(first, others);
            return this;
        }

        public Builder withSupportedUrlActions(
                @NonNull final EnumSet<UrlAction> supportedUrlActions) {
            this.supportedUrlActions = EnumSet.copyOf(supportedUrlActions);
            return this;
        }

        public Builder withResultActions(@NonNull final ResultActions resultActions) {
            this.resultActions = resultActions;
            return this;
        }


        public UrlHandler build() {
            return new UrlHandler(supportedUrlActions, resultActions);
        }
    }

    private static final ResultActions EMPTY_CLICK_LISTENER = new ResultActions() {
        @Override
        public void urlHandlingSucceeded(@NonNull String url, @NonNull UrlAction urlAction) { }
        @Override
        public void urlHandlingFailed(@NonNull String url, @NonNull UrlAction lastFailedUrlAction) { }
    };


    @NonNull
    private EnumSet<UrlAction> mSupportedUrlActions;
    @NonNull
    private ResultActions mResultActions;
    private boolean mAlreadySucceeded;
    private boolean mTaskPending;

    private UrlHandler(
            @NonNull final EnumSet<UrlAction> supportedUrlActions,
            @NonNull final ResultActions resultActions) {
        mSupportedUrlActions = EnumSet.copyOf(supportedUrlActions);
        mResultActions = resultActions;
        mAlreadySucceeded = false;
        mTaskPending = false;
    }

    @NonNull
    EnumSet<UrlAction> getSupportedUrlActions() {
        return EnumSet.copyOf(mSupportedUrlActions);
    }

    @NonNull
    ResultActions getResultActions() {
        return mResultActions;
    }

    public void handleUrl(@NonNull final Context context, @NonNull final String destinationUrl) {
        Preconditions.checkNotNull(context);

        handleUrl(context, destinationUrl, true);
    }

    public void handleUrl(@NonNull final Context context, @NonNull final String destinationUrl,
                          final boolean fromUserInteraction) {
        Preconditions.checkNotNull(context);

        handleUrl(context, destinationUrl, fromUserInteraction, null);
    }

    public void handleUrl(@NonNull final Context context, @NonNull final String destinationUrl,
                          final boolean fromUserInteraction, @Nullable final Iterable<String> trackingUrls) {
        Preconditions.checkNotNull(context);

        if (TextUtils.isEmpty(destinationUrl)) {
            failUrlHandling(destinationUrl, null, "Attempted to handle empty url.", null);
            return;
        }

        final UrlResolutionTask.UrlResolutionListener urlResolutionListener = new UrlResolutionTask.UrlResolutionListener() {
            @Override
            public void onSuccess(@NonNull final String resolvedUrl) {
                mTaskPending = false;
                handleResolvedUrl(context, resolvedUrl, fromUserInteraction, trackingUrls);
            }

            @Override
            public void onFailure(@NonNull final String message,
                                  @Nullable final Throwable throwable) {
                mTaskPending = false;
                failUrlHandling(destinationUrl, null, message, throwable);

            }

        };

        UrlResolutionTask.getResolvedUrl(destinationUrl, urlResolutionListener);
        mTaskPending = true;
    }

    public boolean handleResolvedUrl(@NonNull final Context context,
                                     @NonNull final String url, final boolean fromUserInteraction,
                                     @Nullable Iterable<String> trackingUrls) {
        if (TextUtils.isEmpty(url)) {
            failUrlHandling(url, null, "Attempted to handle empty url.", null);
            return false;
        }

        UrlAction lastFailedUrlAction = UrlAction.NOOP;
        final Uri destinationUri = Uri.parse(url);

        for (final UrlAction urlAction : mSupportedUrlActions) {
            if (urlAction.shouldTryHandlingUrl(destinationUri)) {
                try {
                    urlAction.handleUrl(UrlHandler.this, context, destinationUri,
                            fromUserInteraction);
                    if (!mAlreadySucceeded && !mTaskPending) {
                        makeTrackingHttpRequest(trackingUrls, context);
                        mResultActions.urlHandlingSucceeded(destinationUri.toString(),
                                urlAction);
                        mAlreadySucceeded = true;
                    }
                    return true;
                } catch (IntentNotResolvableException e) {
                    Logger.d(e.getMessage(), e);
                    lastFailedUrlAction = urlAction;
                    // continue trying to match...
                }
            }
        }
        failUrlHandling(url, lastFailedUrlAction, "Link ignored. Unable to handle url: " + url, null);
        return false;
    }

    private void failUrlHandling(@Nullable final String url, @Nullable UrlAction urlAction,
                                 @NonNull final String message, @Nullable final Throwable throwable) {
        Preconditions.checkNotNull(message);

        if (urlAction == null) {
            urlAction = UrlAction.NOOP;
        }

        Logger.d(message, throwable);
        mResultActions.urlHandlingFailed(url, urlAction);
    }

}
